﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media.Imaging;

namespace BadOCR.Common
{
    public class ClipboardMonitor : IDisposable
    {
        private const int WM_CLIPBOARDUPDATE = 0x031D;

        private HwndSource _hwndSource;

        public ClipboardMonitor(IntPtr hwnd)
        {
            _hwndSource = HwndSource.FromHwnd(hwnd);

            _hwndSource.AddHook(WndProc);
            NativeMethods.AddClipboardFormatListener(_hwndSource.Handle);
        }

        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
        {
            if (msg == WM_CLIPBOARDUPDATE)
            {
                // 1、获取剪贴板原始数据
                IDataObject rawDataSource;
                int retryCount = 0;

                while (true)
                {
                    try
                    {
                        rawDataSource = Clipboard.GetDataObject();
                        break;
                    }
                    catch (ExternalException)
                    {
                        if (++retryCount > 5)
                            throw;
                        System.Threading.Thread.Sleep(100);
                    }
                }

                // 2、原始数据 -> 指定格式的数据
                try
                {
                    var formats = rawDataSource.GetFormats();

                    if (rawDataSource.GetDataPresent(DataFormats.FileDrop))
                    {
                        var files = (string[])rawDataSource.GetData(DataFormats.FileDrop);

                        OnClipboardUpdated?.Invoke(this, new ClipboardUpdateEventArgs()
                        {
                            Kind = ClipboardContentKind.File,
                            Files = files
                        });
                    }
                    else if (rawDataSource.GetDataPresent(DataFormats.Text) || rawDataSource.GetDataPresent(DataFormats.UnicodeText))
                    {
                        var text = rawDataSource.GetData(DataFormats.UnicodeText).ToString();

                        OnClipboardUpdated?.Invoke(this, new ClipboardUpdateEventArgs()
                        {
                            Kind = ClipboardContentKind.Text,
                            Text = text
                        });
                    }
                    else if (rawDataSource.GetDataPresent(DataFormats.Bitmap))
                    {
                        //var image = Clipboard.GetImage();
                        //var image = rawDataSource.GetData(DataFormats.Bitmap) as BitmapSource;


                        //string filePath = Path.Combine("d:/aas.jpg");
                        //using (var file = File.Create(filePath))
                        //{
                        //    JpegBitmapEncoder encoder = new JpegBitmapEncoder();
                        //    encoder.Frames.Add(BitmapFrame.Create(Clipboard.GetImage()));
                        //    encoder.Save(file);
                        //}

                        //OnClipboardUpdated?.Invoke(this, new ClipboardUpdateEventArgs()
                        //{
                        //    Kind = ClipboardContentKind.Image,
                        //    Image = image
                        //});
                    }
                    else
                    {
                        //throw new Exception($"暂不支持的数据格式 {formats}");
                        OnClipboardUpdated?.Invoke(this, new ClipboardUpdateEventArgs()
                        {
                            Kind = ClipboardContentKind.UnKnown,
                            Text = $"暂不支持的数据格式 {formats}"
                        });
                    }
                }
                catch (AccessViolationException)
                {
                    // Use-cases such as Remote Desktop usage might throw this exception. 
                    // Applications with Administrative privileges can however override  
                    // this exception when run in a production environment.
                }
                catch (NullReferenceException) { }
            }
            return IntPtr.Zero;
        }

        public void Dispose()
        {
            NativeMethods.RemoveClipboardFormatListener(_hwndSource.Handle);
            _hwndSource.RemoveHook(WndProc);
        }

        public event EventHandler<ClipboardUpdateEventArgs> OnClipboardUpdated;
    }

    class NativeMethods
    {
        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool AddClipboardFormatListener(IntPtr hwnd);

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool RemoveClipboardFormatListener(IntPtr hwnd);
    }

    public class ClipboardUpdateEventArgs : EventArgs
    {
        public ClipboardContentKind Kind { get; set; }

        public string Text { get; set; }

        public BitmapSource Image { get; set; }

        public string[] Files { get; set; }
    }

    public enum ClipboardContentKind
    {
        UnKnown = 0,
        Text = 1,
        Image = 2,
        File = 3
    }
}
